extends Node

# 存储对话历史，用于多轮上下文（初始包含系统提示）
# 修改原系统提示词为：
var conversation = [
	{
		"role": "system",
		"content": """#### 定位
- 桌面智能助手
- 主要任务：进行知识问答和生活建议

#### 能力
- 情绪识别表达：[emotion=X] (X 为 happy/confused/focused/boring/backoff)
- 回答精简：2-3 句话
- 趣味类比：每次回答包含有趣的比喻
- 单表情表达：每个回答用 1 个贴切 emoji

#### 使用方式
- 用户输入问题，助手根据上下文给出回应
- 回答以 [emotion=X] 开头，紧接核心答案，辅以类比描述，加入 1 个相关 emoji
- 禁止输出多个 emotion 标签，不能使用多个 emoji
"""
	}
]

@export var type_delay := 0.05       # 打字机效果的字符延迟（秒）

var api_key = ""

func _ready():
	_load_api_key()
	
	# 连接按钮的 pressed 信号
	#$HelloButton.pressed.connect(_on_HelloButton_pressed)
	$WeatherButton.pressed.connect(_on_WeatherButton_pressed)
	# 连接 HTTPRequest 的完成回调
	#$CreateDialog.request_completed.connect(_on_create_dialog_completed)
	#$AskQuestion.request_completed.connect(_on_ask_question_completed)
	$SendButton.pressed.connect(_on_send_button_pressed)
	$UserInput.text_submitted.connect(_on_send_enter_pressed)
	$AskQuestion.request_completed.connect(_on_ask_question_completed)


func _load_api_key():
	var config = ConfigFile.new()
	var err = config.load("res://config/secret.cfg")
	if err != OK:
		push_error("❌ 无法加载 API 密钥配置文件！请确保 config/secret.cfg 存在。")
		return
	api_key = config.get_value("deepseek", "api_key", "")
	if api_key == "":
		push_error("❌ 配置文件中未找到 api_key！")

## 当用户按下 Hello 按钮时调用
#func _on_HelloButton_pressed():
	## 添加用户消息到对话历史
	#conversation.append({"role": "user", "content": "帮我简单介绍下二叉树，他有那些应用"})
	## 发起 DeepSeek 对话请求
	#call_api($CreateDialog)

# 当用户按下 Weather 按钮时调用
func _on_WeatherButton_pressed():
	conversation.append({"role": "user", "content": "What's the weather?"})
	call_api($AskQuestion)
# 点击按钮发送提问
func _on_send_button_pressed():
	var content = $UserInput.text.strip_edges()
	if content != "":
		conversation.append({"role": "user", "content": content})
		$UserInput.text = ""
		call_api($AskQuestion)

# 回车触发发送
func _on_send_enter_pressed(new_text):
	_on_send_button_pressed()

# 发送 HTTP 请求的通用方法
func call_api(request_node):
	var url = "https://api.deepseek.com/chat/completions"
	# 设置 HTTP 头部：Content-Type 和 Authorization
	var headers = [
		"Content-Type: application/json",
		"Authorization: Bearer " + api_key
	]
	# 构造请求数据字典：model、messages（包含对话历史）、stream=false
	var data = {
		"model": "deepseek-chat",
		"messages": conversation,
		"stream": false
	}
	# 序列化为 JSON 字符串并发送 POST 请求:contentReference[oaicite:6]{index=6}:contentReference[oaicite:7]{index=7}
	var body = JSON.stringify(data)
	request_node.request(url, headers, HTTPClient.METHOD_POST, body)

## CreateDialog 请求完成后的回调
#func _on_create_dialog_completed(result, response_code, headers, body):
	#if result == HTTPRequest.RESULT_SUCCESS and response_code == 200:
		## 解析返回的 JSON 字符串为字典:contentReference[oaicite:8]{index=8}:contentReference[oaicite:9]{index=9}
		#var json = JSON.new()
		#var err = json.parse(body.get_string_from_utf8())
		#if err == OK:
			#var resp = json.data
			## 从 choices[0].message.content 获取 AI 回答文本:contentReference[oaicite:10]{index=10}
			#var assistant_msg = resp["choices"][0]["message"]["content"]
			## 更新 Label 显示内容
			## 在获取 assistant_msg 后添加：
			#var emotion = "happy" # 默认表情
			#var response_text = assistant_msg
#
			## 使用正则表达式匹配表情标签
			#var regex = RegEx.new()
			#regex.compile("\\[emotion=(\\w+)\\]\\n")
			#result = regex.search(assistant_msg)
#
			#if result:
				#emotion = result.get_string(1)
				#response_text = assistant_msg.replace(result.get_string(0), "")
				#$AnimatedSprite2D.play(emotion)
				## 这里添加你的表情更新逻辑，比如：
				## update_emotion(emotion) 
				## 需要您自己实现根据emotion参数更新桌宠动画的方法
#
			#$Label.text = response_text
			#$Label.text = assistant_msg
			## 将 AI 回答加入对话历史，保持上下文
			#conversation.append({"role": "assistant", "content": assistant_msg})
		#else:
			#push_error("JSON 解析错误: " + json.get_error_message())
	#else:
		#push_error("HTTP 请求失败，结果：" + str(result) + "，响应码：" + str(response_code))

# AskQuestion 请求完成后的回调（逻辑与上面相同）
func _on_ask_question_completed(result, response_code, headers, body):
	if result == HTTPRequest.RESULT_SUCCESS and response_code == 200:
		# 解析返回的 JSON 字符串为字典:contentReference[oaicite:8]{index=8}:contentReference[oaicite:9]{index=9}
		var json = JSON.new()
		var err = json.parse(body.get_string_from_utf8())
		if err == OK:
			var resp = json.data
			# 从 choices[0].message.content 获取 AI 回答文本:contentReference[oaicite:10]{index=10}
			var assistant_msg = resp["choices"][0]["message"]["content"]
			# 更新 Label 显示内容
			# 在获取 assistant_msg 后添加：
			var emotion = "happy" # 默认表情
			var response_text = assistant_msg

			# 使用正则表达式匹配表情标签
			var regex = RegEx.new()
			regex.compile("\\[emotion=(\\w+)\\]\\n")
			result = regex.search(assistant_msg)

			if result:
				emotion = result.get_string(1)
				response_text = assistant_msg.replace(result.get_string(0), "")
				$emotion_sprite.play(emotion)
				# 这里添加你的表情更新逻辑，比如：
				# update_emotion(emotion) 
				# 需要您自己实现根据emotion参数更新桌宠动画的方法

			$Label.text = response_text
			$Label.text = assistant_msg
			# 将 AI 回答加入对话历史，保持上下文
			conversation.append({"role": "assistant", "content": assistant_msg})
		else:
			push_error("JSON 解析错误: " + json.get_error_message())
	else:
		push_error("HTTP 请求失败，结果：" + str(result) + "，响应码：" + str(response_code))


# 提取情绪标签和纯文本内容
func extract_emotion(text: String) -> Array:
	var emotion = ""                 # 用于存储提取到的情绪标签
	var content = text  # 默认情况下，纯文本内容即为原始输入

	# 支持英文方括号 [ ] 或中文方括号 【 】 作为标签格式
	# 首先尝试查找英文 '['，如未找到再尝试中文 '【'
	var start_idx = text.find("[")
	if start_idx == -1:
		start_idx = text.find("【")

	if start_idx != -1:
		# 找到左括号后，查找对应的右括号	
		var end_idx = text.find("]", start_idx)
		if end_idx == -1:
			end_idx = text.find("】", start_idx)
		
		if end_idx != -1:
			# 提取标签内容（不包含括号本身）
			emotion = text.substr(start_idx + 1, end_idx - start_idx - 1)
			# 提取标签后面的所有文本作为回复内容
			content = text.substr(end_idx + 1)
	return [emotion, content]
	
	
	# 使用打字机效果逐字显示文本到 Label
# 参数: text - 需要显示的纯文本内容
#      delay - 单个字符显示的时间间隔，默认为 type_delay
#      clear - 是否在显示前清空 Label 文本（默认 true）。如果为 false，则在现有文本后追加。
func display_response(text: String, delay: float = type_delay, clear: bool = true) -> void:
	if clear:
		# 清空 Label 文本，准备显示新内容
		$label.text = ""
	# 循环输出每个字符
	for c in text:
		$label.text += c
		# 等待 delay 秒后再输出下一个字符
		await get_tree().create_timer(delay).timeout
	# 文本完全显示后，可在此处添加额外逻辑（如停止打字动画）
	
	## 示例：使用 HTTPClient 处理 SSE 流式数据
#func send_request_stream():
	#var url = "https://api.deepseek.com/chat/completions"  # 替换为 DeepSeek API
	#var headers = [
		#"Content-Type: application/json",
		#"Accept: text/event-stream",
		#"Authorization: Bearer your_api_key_here"
	#]
	#
	#var messages = [
		#{"role": "system", "content": "You are a helpful assistant."},
		#{"role": "user", "content": "你好"}
	#]
	#var payload = {
		#"model": "deepseek-chat",
		#"messages": messages,
		#"stream": true
	#}
	#var json_data = JSON.stringify(payload).to_utf8_buffer()
#
	#var client := HTTPClient.new()
	#var err := client.connect_to_host("api.deepseek.com", 443)  # 使用 HTTPS
	#if err != OK:
		#print("连接失败：", err)
		#return
#
	## 等待连接完成（最多尝试 3 秒）
	#var timeout := 3.0
	#while client.get_status() == HTTPClient.STATUS_CONNECTING and timeout > 0:
		#client.poll()
		#await get_tree().create_timer(0.1).timeout
		#timeout -= 0.1
	#
	#if client.get_status() != HTTPClient.STATUS_CONNECTED:
		#print("连接超时或失败")
		#return
	#
	#client.request(HTTPClient.METHOD_POST, "/chat/completions", headers, json_data)
#
	## 读取响应头（必须执行）
	#while client.get_status() == HTTPClient.STATUS_REQUESTING:
		#client.poll()
		#await get_tree().create_timer(0.05).timeout
	#
	#if client.has_response():
		#client.read_response_headers()
#
	## 循环读取数据流
	#while true:
		#client.poll()
		#var status := client.get_status()
#
		#if status == HTTPClient.STATUS_BODY:
			#var chunk := client.read_response_body_chunk()
			#if chunk.size() == 0:
				#await get_tree().create_timer(0.05).timeout
				#continue
#
			#var text_chunk := chunk.get_string_from_utf8()
#
			#for event_str in text_chunk.split("\n\n"):
				#if event_str.begins_with("data:"):
					#var json_str := event_str.substr(5).strip_edges()
					#if json_str == "[DONE]":
						#return
#
					#var parse := JSON.parse_string(json_str)
					#if parse.error == OK:
						#var data := parse.result
						#if data.has("content"):
							#var data_text := data["content"]
							#var result := extract_emotion(data_text)
							#var em := result[0]
							#var cont := result[1]
#
							#if em != "":
								#$emotion_sprite.animation = em
								#$emotion_sprite.play()
#
							#display_response(cont, type_delay, false)
#
		#elif status in [0, 5]:  # 0=DISCONNECTED, 5=DONE
			#break
